package com.yeskery.nut.aop;

import com.yeskery.nut.annotation.aop.*;
import com.yeskery.nut.annotation.async.Async;
import com.yeskery.nut.annotation.transaction.Transactional;
import com.yeskery.nut.aop.aspect.AspectAdvice;
import com.yeskery.nut.aop.handler.*;
import com.yeskery.nut.bean.ApplicationContext;
import com.yeskery.nut.core.Order;
import com.yeskery.nut.util.BeanUtils;
import com.yeskery.nut.util.ClassUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 默认的代理对象上下文
 * @author sprout
 * @version 1.0
 * 2022-08-28 12:41
 */
public class DefaultProxyObjectContext implements ProxyObjectContext {

    /** 代理对象工厂map */
    private final Map<Class<? extends Annotation>, ProxyMethodHandler> proxyMethodHandlerMap = new HashMap<>();

    /** 是否支持cglib动态代理 */
    private final boolean enableCglib;

    /** 应用上下文 */
    private final ApplicationContext applicationContext;

    /** 代理方法切面增强点缓存map */
    private final Map<String, Collection<AspectAdvice>> proxyMethodAspectCacheMap = new HashMap<>(64);

    /**
     * 构建默认的代理对象上下文
     * @param applicationContext 应用上下文
     */
    public DefaultProxyObjectContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;

        // 注册支持的代理注解
        proxyMethodHandlerMap.put(Before.class, new BeforeProxyMethodHandler(applicationContext));
        proxyMethodHandlerMap.put(After.class, new AfterProxyMethodHandler(applicationContext));
        proxyMethodHandlerMap.put(Around.class, new AroundProxyMethodHandler(applicationContext));
        proxyMethodHandlerMap.put(Throwing.class, new ThrowingProxyMethodHandler(applicationContext));
        proxyMethodHandlerMap.put(Transactional.class, new TransactionProxyMethodHandler(applicationContext));
        enableCglib = ClassUtils.isExistTargetClass("net.sf.cglib.proxy.Enhancer");
    }

    @Override
    public boolean shouldProxy(Method method) {
        if (!isValidProxyMethod(method)) {
            return false;
        }
        return doShouldProxy(method);
    }

    @Override
    public boolean shouldProxy(Class<?> clazz) {
        if (doShouldProxy(clazz)) {
            return true;
        }
        BeanUtils.ClassDeclaredMetadata declaredMetadata = BeanUtils.getDeclaredMetadata(clazz);
        for (Class<?> declaredClass : declaredMetadata.getDeclaredClasses()) {
            if (doShouldProxy(declaredClass)) {
                return true;
            }
        }
        for (Class<?> declaredInterface : declaredMetadata.getDeclaredInterfaces()) {
            if (doShouldProxy(declaredInterface)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public List<ProxyMethodHandler> getAdapterProxyMethodHandlers(Method method) {
        return proxyMethodHandlerMap.values()
                .stream()
                .filter(h -> h.support(method))
                .sorted(Comparator.comparing(Order::getOrder))
                .collect(Collectors.toList());
    }

    @Override
    public Object createProxyObject(Object object) {
        return createProxyObject(object.getClass(), createWrapInvocationHandler(object));
    }

    @Override
    public Object createProxyObject(Object object, ProxyType proxyType) {
        return createProxyObject(object.getClass(), proxyType, createWrapInvocationHandler(object));
    }

    @Override
    public Object createProxyObject(Object object, Class<?>[] argumentTypes, Object[] arguments, ProxyType proxyType) {
        return createProxyObject(object.getClass(), argumentTypes, arguments, proxyType, createWrapInvocationHandler(object));
    }

    @Override
    public Object createProxyObject(Class<?> clazz, WrapInvocationHandler wrapInvocationHandler) {
        return getAutoProxyObjectFactory(clazz, wrapInvocationHandler).createProxyInstance(clazz);
    }

    @Override
    public Object createProxyObject(Class<?> clazz, ProxyType proxyType, WrapInvocationHandler wrapInvocationHandler) {
        return getSpecifiedProxyObjectFactory(clazz, proxyType, wrapInvocationHandler).createProxyInstance(clazz);
    }

    @Override
    public Object createProxyObject(Class<?> clazz, Class<?>[] argumentTypes, Object[] arguments,
                                    WrapInvocationHandler wrapInvocationHandler) {
        return getAutoProxyObjectFactory(clazz, wrapInvocationHandler).createProxyInstance(clazz, argumentTypes, arguments);
    }

    @Override
    public Object createProxyObject(Class<?> clazz, Class<?>[] argumentTypes, Object[] arguments, ProxyType proxyType,
                                    WrapInvocationHandler wrapInvocationHandler) {
        return getSpecifiedProxyObjectFactory(clazz, proxyType, wrapInvocationHandler)
                .createProxyInstance(clazz, argumentTypes, arguments);
    }

    /**
     * 获取代理方法切面增强点缓存map
     * @return 代理方法切面增强点缓存map
     */
    public Map<String, Collection<AspectAdvice>> getPoxyMethodAspectCacheMap() {
        return proxyMethodAspectCacheMap;
    }

    /**
     * 自动获取代理对象工厂
     * @param clazz 代理对象类型
     * @param wrapInvocationHandler 包装的拦截处理器
     * @return 自动获取代理对象工厂
     */
    private ProxyObjectFactory getAutoProxyObjectFactory(Class<?> clazz, WrapInvocationHandler wrapInvocationHandler) {
        // 优先使用cglib
        if (enableCglib) {
            if (clazz.isInterface()) {
                return new JdkProxyObjectFactory(wrapInvocationHandler);
            }
            return new CglibProxyObjectFactory(wrapInvocationHandler);
        }
        Class<?>[] interfaces = clazz.getInterfaces();
        if (interfaces.length == 0) {
            throw new ProxyObjectCreateException("The Proxy Object Has No Implement Interface, But Not Found The Cglib Library.");
        } else {
            return new JdkProxyObjectFactory(wrapInvocationHandler);
        }
    }

    /**
     * 获取指定的代理对象工厂
     * @param clazz 代理对象类型
     * @param proxyType 代理类型
     * @param wrapInvocationHandler 包装的拦截处理器
     * @return 指定的代理对象工厂
     */
    private ProxyObjectFactory getSpecifiedProxyObjectFactory(Class<?> clazz, ProxyType proxyType,
                                                              WrapInvocationHandler wrapInvocationHandler) {
        if (proxyType == ProxyType.AUTO) {
            return getAutoProxyObjectFactory(clazz, wrapInvocationHandler);
        } if (proxyType == ProxyType.JDK) {
            if (!clazz.isInterface() && clazz.getInterfaces().length == 0) {
                throw new ProxyObjectCreateException("The JDK Proxy Need Implement Interfaces.");
            }
            return new JdkProxyObjectFactory(wrapInvocationHandler);
        } else if (proxyType == ProxyType.CGLIB) {
            if (!enableCglib) {
                throw new ProxyObjectCreateException("Not Found The Cglib Library.");
            }
            return new CglibProxyObjectFactory(wrapInvocationHandler);
        }
        throw new ProxyObjectCreateException("UnSupport Proxy Type [" + proxyType + "]");
    }

    /**
     * 创建包装的拦截处理器
     * @param object 代理对象
     * @return 包装的拦截处理器
     */
    private WrapInvocationHandler createWrapInvocationHandler(Object object) {
        return isAsyncProxyObject(object)
                ? new AsyncWrapInvocationHandler(this, object, applicationContext)
                : new DefaultWrapInvocationHandler(this, object);
    }

    /**
     * 是否是异步代理对象
     * @param object 代理对象
     * @return 是否是异步代理对象
     */
    private boolean isAsyncProxyObject(Object object) {
        for (Method method : object.getClass().getDeclaredMethods()) {
            if (method.isAnnotationPresent(Async.class)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 执行是否需要代理该类对象的方法
     * @param clazz 类对象
     * @return 是否需要代理该类对象的方法
     */
    private boolean doShouldProxy(Class<?> clazz) {
        // 切面配置类不需要再次代理
        if (clazz.isAnnotationPresent(Aspect.class)) {
            return false;
        }
        for (Method method : clazz.getDeclaredMethods()) {
            if (shouldProxy(method)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 执行是否需要代理该方法
     * @param method 方法对象
     * @return 是否需要代理该方法
     */
    private boolean doShouldProxy(Method method) {
        return proxyMethodAspectCacheMap.containsKey(method.toString())
                || method.isAnnotationPresent(Async.class)
                || method.isAnnotationPresent(Compose.class)
                || proxyMethodHandlerMap.keySet().stream().anyMatch(method::isAnnotationPresent);
    }

    /**
     * 是否是有效的代理方法
     * @param method 方法对象
     * @return 是否是有效的代理方法
     */
    private boolean isValidProxyMethod(Method method) {
        int modifiers = method.getModifiers();
        return !Modifier.isStatic(modifiers) && !Modifier.isAbstract(modifiers) && Modifier.isPublic(modifiers);
    }
}
